﻿#pragma once
#include "yzrutil.h"
#include "array/Array.hpp"
#include "io/InputStream.h"

namespace yzrilyzr_util{
	EBCLASS(XMFile){
	public:
	static constexpr int const SAMPLE_NAME_LENGTH=22;
	static constexpr int const INSTRUMENT_HEADER_LENGTH=263;
	static constexpr int const INSTRUMENT_NAME_LENGTH=22;
	static constexpr int const MODULE_NAME_LENGTH=20;
	static constexpr int const TRACKER_NAME_LENGTH=20;
	static constexpr int const PATTERN_ORDER_TABLE_LENGTH=256;
	static constexpr int const NUM_NOTES=96;
	static constexpr int const NUM_ENVELOPE_POINTS=12;
	static constexpr int const MAX_NUM_ROWS=256;
	static constexpr int const DEFAULT_PATTERN_LENGTH=64;
	static constexpr int const XM_SAMPLE_RAMPING_POINTS=0x20;
	enum Loop{
		XM_NO_LOOP,
		XM_FORWARD_LOOP,
		XM_PING_PONG_LOOP,
	};
	enum Frequency{
		XM_LINEAR_FREQUENCIES,
		XM_AMIGA_FREQUENCIES,
	};
	enum Waveform{
		XM_SINE_WAVEFORM=0,
		XM_RAMP_DOWN_WAVEFORM=1,
		XM_SQUARE_WAVEFORM=2,
		XM_RANDOM_WAVEFORM=3,
		XM_RAMP_UP_WAVEFORM=4
	};
	EBCLASS(EffectType){
	public:
	static constexpr int const ARPEGGIO=0;
	static constexpr int const PORTAMENTO_UP=1;
	static constexpr int const PORTAMENTO_DOWN=2;
	static constexpr int const TONE_PORTAMENTO=3;
	static constexpr int const VIBRATO=4;
	static constexpr int const PORTAMENTO_VOLUME_SLIDE=5;
	static constexpr int const VIBRATO_VOLUME_SLIDE=6;
	static constexpr int const TREMOLO=7;
	static constexpr int const SET_PANNING=8;
	static constexpr int const SAMPLE_OFFSET=9;
	static constexpr int const VOLUME_SLIDE=10;
	static constexpr int const POSITION_JUMP=11;
	static constexpr int const SET_VOLUME=12;
	static constexpr int const PATTERN_BREAK=13;
	static constexpr int const SET_SPEED=15;
	};
	EBCLASS(CC){
	public:
	static constexpr int const ARPEGGIO=128;
	static constexpr int const PORTAMENTO_UP=129;
	static constexpr int const PORTAMENTO_DOWN=130;
	static constexpr int const TONE_PORTAMENTO=131;
	static constexpr int const VIBRATO=132;
	static constexpr int const PORTAMENTO_VOLUME_SLIDE=133;
	static constexpr int const VIBRATO_VOLUME_SLIDE=134;
	static constexpr int const TREMOLO=135;
	static constexpr int const SET_PANNING=136;
	static constexpr int const SAMPLE_OFFSET=137;
	static constexpr int const VOLUME_SLIDE=138;
	static constexpr int const POSITION_JUMP=139;
	static constexpr int const SET_VOLUME=140;
	static constexpr int const PATTERN_BREAK=141;
	static constexpr int const SET_SPEED=142;
	};
	struct PatternSlot{
		int note=0; /* 1-96, 97 = Key Off note */
		int instrument=0; /* 1-128 */
		int volume_column=0;
		int effect_type=0;
		int effect_param=0;
	};
	struct EnvelopPoint{
		int frame=0;
		int value=0;
	};
	struct Envelop{
		std::vector<EnvelopPoint>  points;
		int num_points=0;
		int sustain_point=0;
		int loop_start_point=0;
		int loop_end_point=0;
		bool enabled=0;
		bool sustain_enabled=0;
		bool loop_enabled=0;
	};
	struct Pattern{
		int num_rows=0;
		std::vector<PatternSlot> slots;
	};
	struct SampleData{
		yzrilyzr_lang::String name;
		int bits=0; /* Either 8 or 16 */
		int length=0;
		int loop_start=0;
		int loop_length=0;
		int loop_end=0;
		float volume=0;
		int finetune=0;
		Loop loop_type=Loop::XM_NO_LOOP;
		float panning=0;
		int relative_note=0;
		long latest_trigger=0;
		yzrilyzr_array::ByteArray  data8;
		yzrilyzr_array::ShortArray  data16;
	};
	struct Instrument{
		yzrilyzr_lang::String name;
		int num_samples=0;
		yzrilyzr_array::ByteArray * sample_of_notes=nullptr;
		Envelop volume_envelope;
		Envelop panning_envelope;
		int vibrato_type=0;
		int vibrato_sweep=0;
		int vibrato_depth=0;
		int vibrato_rate=0;
		int volume_fadeout=0;
		long latest_trigger=0;
		bool muted=0;
		std::vector< SampleData>  samples;
	};

	EBCLASS(Module){
		public:
		Module() :pattern_table(XMFile::PATTERN_ORDER_TABLE_LENGTH){}
		yzrilyzr_lang::String name;
		yzrilyzr_lang::String trackerName;
		int length=0;
		int restart_position=0;
		int num_channels=0;
		int num_patterns=0;
		int num_instruments=0;
		short tempo=0;
		short bpm=0;
		Frequency frequency_type;
		yzrilyzr_array::ByteArray pattern_table;
		std::vector<Pattern> patterns;
		std::vector<Instrument> instruments;
	};
	static std::shared_ptr<Module> parse(yzrilyzr_io::InputStream & inputStream);
	};
}